
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "SkCornerPathEffect.h"
#include "SkCullPoints.h"
#include "SkGradientShader.h"
#include "SkPath.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkUtils.h"
#include "SkRandom.h"

static void addbump(SkPath* path, const SkPoint pts[2], SkScalar bump)
    {
    SkVector    tang;

    tang.setLength(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY, bump);

    path->lineTo(SkScalarHalf(pts[0].fX + pts[1].fX) - tang.fY,
                 SkScalarHalf(pts[0].fY + pts[1].fY) + tang.fX);
    path->lineTo(pts[1]);
    }

static void subdivide(SkPath* path, SkScalar bump)
    {
    SkPath::Iter    iter(*path, false);
    SkPoint         pts[4];
    SkPath          tmp;

    for (;;)
        switch (iter.next(pts))
            {
            case SkPath::kMove_Verb:
                tmp.moveTo(pts[0]);
                break;
            case SkPath::kLine_Verb:
                addbump(&tmp, pts, bump);
                bump = -bump;
                break;
            case SkPath::kDone_Verb:
                goto FINISH;
            default:
                break;
            }

FINISH:
    path->swap(tmp);
    }

static SkIPoint* getpts(const SkPath& path, int* count)
    {
    SkPoint     pts[4];
    int         n = 1;
    SkIPoint*   array;

        {
        SkPath::Iter    iter(path, false);
        for (;;)
            switch (iter.next(pts))
                {
                case SkPath::kLine_Verb:
                    n += 1;
                    break;
                case SkPath::kDone_Verb:
                    goto FINISHED;
                default:
                    break;
                }
        }

FINISHED:
    array = new SkIPoint[n];
    n = 0;

        {
        SkPath::Iter    iter(path, false);
        for (;;)
            switch (iter.next(pts))
                {
                case SkPath::kMove_Verb:
                    array[n++].set(SkScalarRound(pts[0].fX), SkScalarRound(pts[0].fY));
                    break;
                case SkPath::kLine_Verb:
                    array[n++].set(SkScalarRound(pts[1].fX), SkScalarRound(pts[1].fY));
                    break;
                case SkPath::kDone_Verb:
                    goto FINISHED2;
                default:
                    break;
                }
        }

FINISHED2:
    *count = n;
    return array;
    }

static SkScalar nextScalarRange(SkRandom& rand, SkScalar min, SkScalar max)
    {
    return min + SkScalarMul(rand.nextUScalar1(), max - min);
    }

class CullView : public SampleView
    {
    public:
        CullView()
            {
            fClip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160));

            SkRandom    rand;

            for (int i = 0; i < 50; i++)
                {
                SkScalar x = nextScalarRange(rand, -fClip.width()*1, fClip.width()*2);
                SkScalar y = nextScalarRange(rand, -fClip.height()*1, fClip.height()*2);
                if (i == 0)
                    fPath.moveTo(x, y);
                else
                    fPath.lineTo(x, y);
                }

            SkScalar bump = fClip.width()/8;
            subdivide(&fPath, bump);
            subdivide(&fPath, bump);
            subdivide(&fPath, bump);
            fPoints = getpts(fPath, &fPtCount);

            this->setBGColor(0xFFDDDDDD);
            }

        virtual ~CullView()
            {
            delete[] fPoints;
            }

    protected:
        // overrides from SkEventSink
        virtual bool onQuery(SkEvent* evt)
            {
            if (SampleCode::TitleQ(*evt))
                {
                SampleCode::TitleR(evt, "Culling");
                return true;
                }
            return this->INHERITED::onQuery(evt);
            }

        virtual void onDrawContent(SkCanvas* canvas)
            {
            SkAutoCanvasRestore ar(canvas, true);

            canvas->translate(  SkScalarHalf(this->width() - fClip.width()),
                                SkScalarHalf(this->height() - fClip.height()));

            //     canvas->scale(SK_Scalar1*3, SK_Scalar1*3, 0, 0);

            SkPaint paint;

            //    paint.setAntiAliasOn(true);
            paint.setStyle(SkPaint::kStroke_Style);

            canvas->drawRect(fClip, paint);

#if 1
            paint.setColor(0xFF555555);
            paint.setStrokeWidth(SkIntToScalar(2));
//        paint.setPathEffect(new SkCornerPathEffect(SkIntToScalar(30)))->unref();
            canvas->drawPath(fPath, paint);
//        paint.setPathEffect(NULL);
#endif

            SkPath  tmp;
            SkIRect iclip;
            fClip.round(&iclip);

            SkCullPointsPath    cpp(iclip, &tmp);

            cpp.moveTo(fPoints[0].fX, fPoints[0].fY);
            for (int i = 0; i < fPtCount; i++)
                cpp.lineTo(fPoints[i].fX, fPoints[i].fY);

            paint.setColor(SK_ColorRED);
            paint.setStrokeWidth(SkIntToScalar(3));
            paint.setStrokeJoin(SkPaint::kRound_Join);
            canvas->drawPath(tmp, paint);

            this->inval(NULL);
            }

    private:
        SkRect      fClip;
        SkIPoint*   fPoints;
        SkPath      fPath;
        int         fPtCount;

        typedef SampleView INHERITED;
    };

//////////////////////////////////////////////////////////////////////////////

static SkView* MyFactory()
    {
    return new CullView;
    }
static SkViewRegister reg(MyFactory);

